package top.lingkang.ciyuanwaf.utils.terminal;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.pty4j.PtyProcess;
import com.pty4j.PtyProcessBuilder;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author lingkang
 * Created by 2023/11/25
 */
@Slf4j
public class TerminalThread {
    private static PtyProcess process;
    private BufferedReader inputReader, errorReader;
    private BufferedWriter outputWriter;
    public List<TerminalEvent> events = new ArrayList<>();

    public TerminalThread() {
        if (process != null)
            throw new IllegalArgumentException("已经初始化实例");
        log.info("init TerminalThread ...");
        try {
            Map<String, String> envs = new HashMap<>(System.getenv());
            envs.put("TERM", "xterm");
            String[] termCommand = null;
            if (OsUtils.isWindow())
                termCommand = "cmd.exe".split("\\s+");
            else
                termCommand = "/bin/sh -i".split("\\s+");
            PtyProcessBuilder ptyProcessBuilder = new PtyProcessBuilder()
                    .setCommand(termCommand)
                    .setEnvironment(envs)
                    .setDirectory(System.getProperty("user.dir"));
            process = ptyProcessBuilder.start();
            inputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8));
            new Thread(() -> redIn(inputReader)).start();
            new Thread(() -> redIn(errorReader)).start();
            outputWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
            ThreadUtil.sleep(500);
            if (OsUtils.isWindow()) {// init
                outputWriter.write("dir\r");
                outputWriter.flush();
            }else {
                outputWriter.write("pwd\r");
                outputWriter.flush();
            }
            Runtime.getRuntime().addShutdownHook(new Thread(this::exit));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void send(String cmd) {
        try {
            if (!cmd.endsWith("\r"))
                cmd += "\r";
            outputWriter.write(cmd);
            outputWriter.flush();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void redIn(BufferedReader reader) {
        try {
            int nRead;
            char[] data = new char[1024];
            while ((nRead = reader.read(data, 0, data.length)) != -1) {
                StringBuilder builder = new StringBuilder(nRead);
                builder.append(data, 0, nRead);
                //发送事件
                receive(builder.toString());
                data = new char[1024];
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void receive(String str) {
        log.info("event number: {} content: {}", events.size(), str);
        events.forEach(terminalEvent -> terminalEvent.receive(str));
    }

    public void addEvent(TerminalEvent... event) {
        events.addAll(Arrays.asList(event));
    }

    public void removeEvent(TerminalEvent... event) {
        Iterator<TerminalEvent> iterator = events.iterator();
        List<TerminalEvent> list = Arrays.asList(event);
        while (iterator.hasNext()) {
            TerminalEvent next = iterator.next();
            if (list.contains(next)) {
                iterator.remove();
            }
        }
    }

    private void exit() {
        log.info("exit app...");
        process.destroy();
        IoUtil.close(inputReader);
        IoUtil.close(errorReader);
        IoUtil.close(outputWriter);
    }


}
